iT邦幫忙

2026 iThome 鐵人賽

DAY 18
0
Modern Web

Vue 演進驗證 × AI Coding 時代的工程實踐系列 第 18

Day 18:大量 UI Rendering 的成本在哪裡?

  • 分享至 

  • xImage
  •  
先建立 Vue 3.5 Baseline,再談 Vue 3.6 是否真的改善

昨天建立了 VDOM Stress Test,同一個 Scenario,分別測試:

100
500
1000
5000 Nodes

今天目的是想 先知道大量 UI Rendering 的成本分布,再拿 Vue 3.5.40 結果與 Vue 3.6.0-rc.2 比較。

如果只記錄一個 Render Duration,我們只能知道這次比較慢;如果要驗證 Vue 版本的效能變化,還需要知道成本大致落在哪個區域:

JavaScript?
Browser Style?
Layout?
Painting?

這也是明天進入 Vue 3.6 Validation 前,需要先建立的 Cost Map

先把一次 UI Render 拆開


一次大量 UI Rendering,可以先用下面這個模型理解:

Application
    │
    ▼
JavaScript / Vue
    │
    ├─ Render
    ├─ VNode Creation
    ├─ Diff / Patch
    └─ DOM Mutation
             │
             ▼
       Browser Pipeline
             │
       ┌─────┼─────┐
       ▼     ▼     ▼
     Style Layout Paint

因此這次 Baseline 主要觀察四個面向:

觀察面向 Chrome / Scenario 指標
JavaScript Scripting
Style Recalculate Style
Layout Layout
Painting Paint
Scenario Render Duration

這裡要先把量測邊界講清楚:Scripting 不等於 Vue Runtime,它可能包含 Application JavaScript、Vue Runtime,以及其他 JavaScript 工作。

同樣地,Layout / Paint 也不能直接視為 Vue Cost。

Render Duration 是 Scenario 自己量到的操作時間,也不能直接拿來和 Chrome Trace 裡各 category 的數字相加。

所以今天建立的,是 Cost Map,我們先觀察 Node 數量增加後,哪些成本項目開始明顯成長?

Mount:第一次建立大量 UI


先看第一次建立 UI 的結果。

Nodes 100 500 1000 5000
Render Duration 4.3 ms 11.8 ms 18.3 ms 63.2 ms
Scripting 9 ms 16 ms 25 ms 91 ms
Recalculate Style 0.6 ms 2.5 ms 4.9 ms 16.9 ms
Layout 4.8 ms 22.4 ms 44.8 ms 218.8 ms
Paint 2.3 ms 5.0 ms 6.7 ms 5.8 ms

Node 數量從 100 增加到 5000 時,Render Duration4.3 ms 增加到 63.2 ms,但更值得注意的是各分類的成長幅度。

不同 Node 數量的 Mount Layout Cost

其中 Layout 從 4.8 ms 增加到 218.8 ms,成長約 45.6 倍,接近 Node 數量的 50 倍成長,這代表在這個 Scenario 中,Mount 階段的主要成本來源逐漸往 Browser Layout 集中。

但這裡有一個很重要的量測細節:

這些數字不是同一個時間分母下可以直接相加的成本。

例如 N=5000 時:

Scenario Render Duration = 63.2 ms
Chrome Layout            = 218.8 ms

Layout 數字大於 Render Duration,並不代表測量錯誤,因為兩者的定義不同:

Render Duration
    ↓
Scenario 自己量測的操作時間

Chrome Trace
    ↓
特定事件 / category 的 trace measurement

因此這裡我們只拿它們觀察成長趨勢,不把它們當成可以互相加總的時間。

為什麼 Layout 成長這麼明顯?

這個 Scenario 的 Card List 使用 CSS Grid:

.cards__list {
  display: grid;
  grid-template-columns: repeat(
    auto-fill,
    minmax(160px, 1fr)
  );
}

當 5,000 個 Card 第一次進入 DOM,Browser 需要處理大量元素的幾何資訊:

5000 Cards
    │
    ▼
Grid calculation
    │
    ▼
Card size / position
    │
    ▼
Layout geometry

因此目前可以確認 大量 UI 第一次 Mount 時,Browser Layout 會隨 Node 數量快速增加。

這也是為什麼只看 Vue / VDOM 還不夠,因為 Vue 完成 Render、VNode 建立與 DOM 操作之後,Browser 還需要處理實際的頁面 Layout。

Update:同樣 5,000 Nodes 成本分布改變


接著測試已經存在的 UI,也就是:

Mount
  ↓
已有 100 / 500 / 1000 / 5000 Nodes
  ↓
Trigger Update

結果如下:

Nodes 100 500 1000 5000
Render Duration 1.5 ms 4.9 ms 8.6 ms 24.9 ms
Scripting 4 ms 8 ms 13 ms 43 ms
Recalculate Style 0.1 ms 0.4 ms 0.1 ms 0.1 ms
Layout 0.2 ms 0.4 ms 0.4 ms 1.0 ms
Paint 0.4 ms 1.1 ms 1.1 ms 1.6 ms

這次最明顯的現象,是 Mount 與 Update 的 Layout 數字差距非常大

Node 數量仍然是 5,000,但 Layout 從 Mount 的 218.8 ms 降到 Update 的 1.0 ms,也就是約 219 倍的差距,如下圖所示:

5000 Nodes Mount 和 Update 的成本結構

換句話說,Node 數量本身不能直接推導每次 Update 都會產生同等規模的 Layout Cost。

Update 到底改了什麼?


這個 Scenario 的 Update 會重新建立一個 Card[]

const next: Card[] = []

for (let i = 1; i <= selectedCount.value; i++) {
  next.push({
    id: i,
    title: `Card #${i}`,
  })
}

cards.value = next

雖然資料陣列重新建立,但新舊資料維持:

相同 key
相同順序
相同數量
相同 DOM 結構

例如:

Old VNode              New VNode

key = 1       ───────→ key = 1
key = 2       ───────→ key = 2
key = 3       ───────→ key = 3
   ...                    ...
key = 5000    ───────→ key = 5000

因此這次 Update 並不是重新建立 5,000 個不同結構的 UI,它更接近:

New Array
    │
    ▼
Reactive Trigger
    │
    ▼
Component Render
    │
    ▼
VNode Creation
    │
    ▼
Reconciliation / Patch
    │
    ▼
必要時更新 DOM

在這個 Scenario 裡,DOM 結構維持穩定,因此 Update 階段沒有出現 Mount 時同等規模的 Layout 成長,代表 不同階段,成本結構可能完全不同

5000 Nodes — Mount vs Update Cost Structure

今天真正建立的是一張 Cost Map


把今天的觀察整理起來:

                 VDOM Stress Test
                        │
             ┌──────────┴──────────┐
             ▼                     ▼
           Mount                  Update
             │                     │
             ▼                     ▼
      Layout 成長明顯        Layout 維持較低
             │                     │
             ▼                     ▼
     Browser Pipeline        JS / Render /
        值得觀察               Patch 值得觀察

但這張圖只代表這次 Vue 3.5.40 初始觀察到的成本分布,但是還不能回答 Vue 3.6 是否改善了其中任何一層?

因為目前的 Scripting 仍然包含多種 JavaScript 工作,Layout / Paint 也屬於 Browser rendering cost。

量測方式也要先留一個邊界


這次 Day 18 的數字,是透過 Chrome DevTools Performance 人工單次錄製與讀值取得,因此它適合用來:

  • 建立初始 Cost Map
  • 觀察 Node 數量增加時的趨勢
  • 找出明天 Validation 值得關注的成本區域

但它不適合直接拿來當作後續 Vue 3.5 / Vue 3.6 的正式 A/B measurement。

後續正式版本比較會使用校準後的自動化 CDP measurement protocol,包含固定的 measurement window、warm-up、multiple trials 與統一的 extraction / aggregation。

所以今天這組數字的角色很明確:

它是 Initial Baseline Observation,不是最後的版本比較數據。

這個區分很重要,否則很容易把不同 measurement protocol 的數字直接放在一起比較。

小結


今天把問題拆開後,可以看到:

Mount

Node ↑
  ↓
Layout 顯著增加

大量 UI 第一次進入 DOM 時,Browser Layout 是值得關注的成本。

Update

Node = 5000

Mount Layout
218.8 ms

        ↓

Update Layout
1.0 ms

相同 Node 數量下,Update 並沒有產生同等規模的 Layout 成本。

同時,Scripting 仍然是 Update 階段值得觀察的主要區域,但目前還不能把它直接等同於 Vue Runtime。

VDOM Stress Test
       │
       ├── Mount
       │     └── Layout 成長明顯
       │
       └── Update
             ├── Scripting 值得觀察
             └── Layout 成本明顯較低

明天再用相同 Scenario 進入 Vue 3.6.0-rc.2 Validation,問題就可以進一步縮小成:哪些成本真的發生變化,以及這些變化能不能歸因到 Vue Runtime。


上一篇
Day 17:建立 VDOM Stress Test Scenario
下一篇
Day 19:Vue 3.6 到底改善了哪一層 Cost?
系列文
Vue 演進驗證 × AI Coding 時代的工程實踐23
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言